package edu.northwestern.cbits.purple_robot_manager; import java.util.Date; import java.util.HashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.AlarmManager; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v7.app.NotificationCompat; import edu.northwestern.cbits.purple_robot_manager.activities.StartActivity; import edu.northwestern.cbits.purple_robot_manager.http.JsonScriptRequestHandler; import edu.northwestern.cbits.purple_robot_manager.http.LocalHttpServer; import edu.northwestern.cbits.purple_robot_manager.http.commands.JSONCommand; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.logging.SanityManager; import edu.northwestern.cbits.purple_robot_manager.plugins.HttpUploadPlugin; import edu.northwestern.cbits.purple_robot_manager.plugins.OutputPlugin; import edu.northwestern.cbits.purple_robot_manager.plugins.OutputPluginManager; import edu.northwestern.cbits.purple_robot_manager.probes.ProbeManager; import edu.northwestern.cbits.purple_robot_manager.probes.builtin.RandomNoiseProbe; import edu.northwestern.cbits.purple_robot_manager.triggers.TriggerManager; public class PersistentService extends Service { public static final String NUDGE_PROBES = "purple_robot_manager_nudge_probe"; public static final String SCRIPT_ACTION = "edu.northwestern.cbits.purplerobot.run_script"; public static final String START_HTTP_SERVICE = "purple_robot_start_http_service"; public static final String STOP_HTTP_SERVICE = "purple_robot_stop_http_service"; public static final String PROBE_NUDGE_INTERVAL = "probe_nudge_interval"; public static final String PROBE_NUDGE_INTERVAL_DEFAULT = "15000"; private LocalHttpServer _httpServer = new LocalHttpServer(); public IBinder onBind(Intent intent) { return null; } @SuppressLint("NewApi") @SuppressWarnings("deprecation") public void onCreate() { super.onCreate(); LogManager.getInstance(this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String title = this.getString(R.string.notify_running_title); String message = this.getString(R.string.notify_running); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, StartActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentTitle(title); builder.setContentText(message); builder.setContentIntent(contentIntent); builder.setSmallIcon(R.drawable.ic_note_normal); builder.setWhen(System.currentTimeMillis()); builder.setColor(0xff4e015c); Notification note = builder.build(); this.startForeground(SanityManager.NOTE_ID, note); AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); PendingIntent pi = PendingIntent.getService(this, 0, new Intent(PersistentService.NUDGE_PROBES), PendingIntent.FLAG_UPDATE_CURRENT); long now = System.currentTimeMillis(); long interval = Long.parseLong(prefs.getString(PersistentService.PROBE_NUDGE_INTERVAL, PersistentService.PROBE_NUDGE_INTERVAL_DEFAULT)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) alarmManager.setExact(AlarmManager.RTC_WAKEUP, now + interval, pi); else alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pi); OutputPlugin.loadPluginClasses(this); if (prefs.getBoolean(LocalHttpServer.BUILTIN_HTTP_SERVER_ENABLED, LocalHttpServer.BUILTIN_HTTP_SERVER_ENABLED_DEFAULT)) this._httpServer.start(this); else this._httpServer.stop(this); BroadcastReceiver scriptReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (intent.hasExtra("response_mode")) { if ("activity".equals(intent.getStringExtra("response_mode"))) { if (intent.hasExtra("package_name") && intent.hasExtra("activity_class")) { String pkgName = intent.getStringExtra("package_name"); String clsName = intent.getStringExtra("activity_class"); Intent response = new Intent(); response.setComponent(new ComponentName(pkgName, clsName)); response.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (intent.hasExtra("command")) { try { JSONObject arguments = new JSONObject(); for (String key : intent.getExtras().keySet()) { arguments.put(key, intent.getStringExtra(key)); } JSONCommand cmd = JsonScriptRequestHandler.commandForJson(arguments, context); JSONObject result = cmd.execute(context); response.putExtra("full_payload", result.toString(2)); JSONArray names = result.names(); for (int i = 0; i < names.length(); i++) { String name = names.getString(i); response.putExtra(name, result.getString(name)); } response.putExtra("full_payload", result.toString(2)); } catch (JSONException e) { LogManager.getInstance(context).logException(e); response.putExtra("error", e.toString()); } } context.startActivity(response); } } } } }; IntentFilter filter = new IntentFilter(); filter.addAction(PersistentService.SCRIPT_ACTION); this.registerReceiver(scriptReceiver, filter); final PersistentService me = this; final Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread thread, Throwable ex) { HashMap<String, Object> payload = new HashMap<>(); payload.put("message", ex.getMessage()); LogManager.getInstance(me).log("pr_app_crashed", payload); handler.uncaughtException(thread, ex); } }); } @SuppressLint("NewApi") public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null) { String action = intent.getAction(); if (NUDGE_PROBES.equals(action)) { ProbeManager.nudgeProbes(this); TriggerManager.getInstance(this).refreshTriggers(this); ScheduleManager.runOverdueScripts(this); OutputPlugin plugin = OutputPluginManager.sharedInstance.pluginForClass(this, HttpUploadPlugin.class); if (plugin instanceof HttpUploadPlugin) { HttpUploadPlugin http = (HttpUploadPlugin) plugin; http.uploadPendingObjects(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); PendingIntent pi = PendingIntent.getService(this, 0, new Intent(PersistentService.NUDGE_PROBES), PendingIntent.FLAG_UPDATE_CURRENT); long now = System.currentTimeMillis(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); long interval = Long.parseLong(prefs.getString(PersistentService.PROBE_NUDGE_INTERVAL, PersistentService.PROBE_NUDGE_INTERVAL_DEFAULT)); alarmManager.setExact(AlarmManager.RTC_WAKEUP, now + interval, pi); } } else if (RandomNoiseProbe.ACTION.equals(action) && RandomNoiseProbe.instance != null) RandomNoiseProbe.instance.isEnabled(this); else if (START_HTTP_SERVICE.equals(action)) this._httpServer.start(this); else if (STOP_HTTP_SERVICE.equals(action)) this._httpServer.stop(this); } return Service.START_STICKY; } public void onTaskRemoved(Intent rootIntent) { LogManager.getInstance(this).log("pr_service_stopped", null); } }